#include"../server.hpp"
#include<fstream>
#include<sys/stat.h>
#include<regex>
#include<cassert>
#include<regex>


#define DEFAULT_TIMEOUT 5
class Util
{
public:
    //字符串分割函数,将分割后的字串都放在arry中！返回的是字串的数量！
    static size_t Split(const std::string &src, const std::string &sep, std::vector<std::string> *arry)
    {
        size_t offset = 0;
        while (offset < src.size())
        {
            size_t pos = src.find(sep, offset); // 在src字符串的偏移量offset处，开始向后sep字符！
            std::string sub = src.substr(offset, pos - offset);
            if (pos == std::string::npos) // 没有找到特定字符！
            {
                arry->push_back(src.substr(offset)); // 将剩下的都当做子串！
                return arry->size();
            }
            if (sub.size() > 0) // 防止aaaa,,,,,,,bbb
                arry->push_back(sub);
            offset = pos + sep.size();
        }
        return arry->size();
    }

    //读取文件所有内容！将读取的内容放入buffer中！
    static bool ReadFile(const std::string& filename, std::string& buf) 
    {

        std::ifstream ifs(filename, std::ios::binary);
        if (ifs.is_open() == false)
        {
            ERR_LOG("OPEN FILe :%s FAILED!", filename.c_str());
            return false;
        }
        // 先获取文件大小！——将文件偏移到末尾减去起始位置就是文件大小！
        size_t fsize = 0;
        ifs.seekg(0, ifs.end); // 从末尾位置开始，偏移0位
        fsize = ifs.tellg();   // 获取
        ifs.seekg(0, ifs.beg); // 跳转回文件的起始地址！

        buf.resize(fsize);
        ifs.read(&buf[0], fsize);
        if (ifs.good() == false) // 表示上一个操作有没有出现问题！
        {
            ERR_LOG("READ %s FILE FAILED!", filename.c_str());
            ifs.close();
            return false;
        }
        ifs.close();
        return true;
    }

    //向文件写入数据！
    static bool WriteFile(const std::string& filename,const std::string &buf)
    {
       std::ofstream ofs(filename,std::ios::binary|std::ios::trunc); //打开文件后将文件清空！
       if(ofs.is_open() == false)
       {
           ERR_LOG("OPEN FILe :%s FAILED!", filename.c_str());
           return false;
       }
       ofs.write(buf.c_str(),buf.size());
       if(ofs.good() == false)
       {
            ERR_LOG("WRITE %s FILE FAILED!");
            ofs.close();
            return false;
       }
       ofs.close();
       return true;
    }

    //URL编码——避免URL中资源路径与查询字符串中的特殊字符与HTTP请求中的特殊字符产生歧义！
    //编码格式：将特殊字符的ascii,转化为两个16进制字符！前缀为 %
    //例如：C++ 转化为C%2B%2B——因为+的ascii是43,转换为16进制就是2B
    //在URL中有几个不编码的通用字符:. - _ ~以及字母和数字！——在FC3986文档中规定！
    //在RFC3986中规定 编码格式为%HH
    //在W3C标准文档中规定查询字符串中的空格需要被编码为+ ,解码则是空格转 +
    static std::string UrlEncode(const std::string& url, bool convert_space_to_plus) // 第二个参数就是是否将空格转化为加号
    {
        std::string res;
        for (auto &c : url)
        { // sialnum函数判断是不是数字字符和大小写字母！
            if (c == '.' || c == '~' || c == '_' || c == '-' || isalnum(c) != 0)
            {
                res += c;
                continue;
            }
            if (c == ' ' && convert_space_to_plus == true)
            {
                res += '+';
                continue;
            }
            // 剩下的字符都是需要编码为%HH格式的
            char tmp[4] = {0};
            snprintf(tmp, 4, "%%%02X", c); //%% 是用来输入正常的% %02X——大写16进制输入！02是用来当做占位符
            res += tmp;
        }
        return res;
    }

    static char HEXTOI(char c)
    {
        if (c >= '0' && c <= '9')
            return c - '0';
        else if (c >= 'a' && c <= 'z')
            return c - 'a' + 10; // a是10，b是11在16进制中！所以我们要加上10！
        else if (c >= 'A' && c <= 'Z')
            return c - 'A' + 10; // A是10，B是11在16进制中！所以我们要加上10！

        return -1;
    }

    // URL解码
    static std::string UrlDecode(const std::string& url, bool convert_space_to_plus) // 第二个参数就是是否将空格转化为加号
    {
        // 遇到%将紧随其后的两个字符转换为数字！——第一个数字左移4位，然后加上第二个数字
        // 例如：+ ——> %2b 转换回来就是 2<<4 + b ,<< 4就是相当于*16
        std::string res;
        for (int i = 0; i < url.size(); i++)
        {
            if (url[i] == '%' && i +2 < url.size()) // 遇到%才进行解码
            {
                char v1 = HEXTOI(url[i + 1]);
                char v2 = HEXTOI(url[i + 2]);
                char v = (v1 << 4) + v2;
                res += v;
                i += 2;
                continue;
            }
            if (url[i] == '+' && convert_space_to_plus == true)
            {
                res += ' ';
                continue;
            }
            res += url[i]; // 没有遇到就正常进行！
        }
        return res;
    }

    //响应状态码的描述符信息的获取！
    static std::string StatusDesc(int status)
    {
        std::unordered_map<int,std::string> status_msg{
            {100, "Continue"},
                {101, "Switching Protocol"},
                {102, "Processing"},
                {103, "Early Hints"},
                {200, "OK"},
                {201, "Created"},
                {202, "Accepted"},
                {203, "Non-Authoritative Information"},
                {204, "No Content"},
                {205, "Reset Content"},
                {206, "Partial Content"},
                {207, "Multi-Status"},
                {208, "Already Reported"},
                {226, "IM Used"},
                {300, "Multiple Choice"},
                {301, "Moved Permanently"},
                {302, "Found"},
                {303, "See Other"},
                {304, "Not Modified"},
                {305, "Use Proxy"},
                {306, "unused"},
                {307, "Temporary Redirect"},
                {308, "Permanent Redirect"},
                {400, "Bad Request"},
                {401, "Unauthorized"},
                {402, "Payment Required"},
                {403, "Forbidden"},
                {404, "Not Found"},
                {405, "Method Not Allowed"},
                {406, "Not Acceptable"},
                {407, "Proxy Authentication Required"},
                {408, "Request Timeout"},
                {409, "Conflict"},
                {410, "Gone"},
                {411, "Length Required"},
                {412, "Precondition Failed"},
                {413, "Payload Too Large"},
                {414, "URI Too Long"},
                {415, "Unsupported Media Type"},
                {416, "Range Not Satisfiable"},
                {417, "Expectation Failed"},
                {418, "I'm a teapot"},
                {421, "Misdirected Request"},
                {422, "Unprocessable Entity"},
                {423, "Locked"},
                {424, "Failed Dependency"},
                {425, "Too Early"},
                {426, "Upgrade Required"},
                {428, "Precondition Required"},
                {429, "Too Many Requests"},
                {431, "Request Header Fields Too Large"},
                {451, "Unavailable For Legal Reasons"},
                {501, "Not Implemented"},
                {502, "Bad Gateway"},
                {503, "Service Unavailable"},
                {504, "Gateway Timeout"},
                {505, "HTTP Version Not Supported"},
                {506, "Variant Also Negotiates"},
                {507, "Insufficient Storage"},
                {508, "Loop Detected"},
                {510, "Not Extended"},
                {511, "Network Authentication Required"}
        };
        auto it = status_msg.find(status);
        if(it != status_msg.end())
            return it->second;
        return "UnKnow";
    }

    //根据文件后缀名获取文件mime
    static std::string ExMime(const std::string& filename)
    {
        std::unordered_map<std::string, std::string> mime_msg
        {

            {".aac", "audio/aac"},
                {".abw", "application/x-abiword"},
                {".arc", "application/x-freearc"},
                {".avi", "video/x-msvideo"},
                {".azw", "application/vnd.amazon.ebook"},
                {".bin", "application/octet-stream"},
                {".bmp", "image/bmp"},
                {".bz", "application/x-bzip"},
                {".bz2", "application/x-bzip2"},
                {".csh", "application/x-csh"},
                {".css", "text/css"},
                {".csv", "text/csv"},
                {".doc", "application/msword"},
                {".docx", "application/vnd.openxmlformats-officedocument.wordprocessingml.document"},
                {".eot", "application/vnd.ms-fontobject"},
                {".epub", "application/epub+zip"},
                {".gif", "image/gif"},
                {".htm", "text/html"},
                {".html", "text/html"},
                {".ico", "image/vnd.microsoft.icon"},
                {".ics", "text/calendar"},
                {".jar", "application/java-archive"},
                {".jpeg", "image/jpeg"},
                {".jpg", "image/jpeg"},
                {".js", "text/javascript"},
                {".json", "application/json"},
                {".jsonld", "application/ld+json"},
                {".mid", "audio/midi"},
                {".midi", "audio/x-midi"},
                {".mjs", "text/javascript"},
                {".mp3", "audio/mpeg"},
                {".mpeg", "video/mpeg"},
                {".mpkg", "application/vnd.apple.installer+xml"},
                {".odp", "application/vnd.oasis.opendocument.presentation"},
                {".ods", "application/vnd.oasis.opendocument.spreadsheet"},
                {".odt", "application/vnd.oasis.opendocument.text"},
                {".oga", "audio/ogg"},
                {".ogv", "video/ogg"},
                {".ogx", "application/ogg"},
                {".otf", "font/otf"},
                {".png", "image/png"},
                {".pdf", "application/pdf"},
                {".ppt", "application/vnd.ms-powerpoint"},
                {".pptx", "application/vnd.openxmlformats-officedocument.presentationml.presentation"},
                {".rar", "application/x-rar-compressed"},
                {".rtf", "application/rtf"},
                {".sh", "application/x-sh"},
                {".svg", "image/svg+xml"},
                {".swf", "application/x-shockwave-flash"},
                {".tar", "application/x-tar"},
                {".tif", "image/tiff"},
                {".tiff", "image/tiff"},
                {".ttf", "font/ttf"},
                {".txt", "text/plain"},
                {".vsd", "application/vnd.visio"},
                {".wav", "audio/wav"},
                {".weba", "audio/webm"},
                {".webm", "video/webm"},
                {".webp", "image/webp"},
                {".woff", "font/woff"},
                {".woff2", "font/woff2"},
                {".xhtml", "application/xhtml+xml"},
                {".xls", "application/vnd.ms-excel"},
                {".xlsx", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"},
                {".xml", "application/xml"},
                {".xul", "application/vnd.mozilla.xul+xml"},
                {".zip", "application/zip"},
                {".3gp", "video/3gpp"},
                {".3g2", "video/3gpp2"},
                {".7z","application/x-7z-compressed"}
        };
        //例如：a.b.txt最后一个点的后面就是后缀！
        size_t pos = filename.find_last_of('.');
        if(pos == std::string::npos)
            return "application/octet-stream";//没有找到就返回文件类型是一个二进制流！

        std::string ext = filename.substr(pos);
        auto it = mime_msg.find(ext);
        if (it == mime_msg.end())
            return "application/octet-stream"; // 没有找到就返回文件类型是一个二进制流！

        return it->second;
    }

    //判断文件是不是一个目录！
    static bool IsDirectory(const std::string& filename)
    {
        struct stat st;
        int ret = stat(filename.c_str(),&st);
        if(ret < 0)
        {
            return false;
        }
        return S_ISDIR(st.st_mode);//这个是一个宏！可以用来帮我们判断是不是一个文件夹！
    }

    //判断文件是不是一个普通文件！
    static bool IsRegular(const std::string& filename)
    {
        struct stat st;
        int ret = stat(filename.c_str(),&st);
        if(ret < 0)
        {
            return false;
        }
        return S_ISREG(st.st_mode);//这个是一个宏！可以用来帮我们判断是不是一个文件夹！
    }

    //http请求的资源的有效性的判读！
    // /index.html ---前边的/叫做相对根目录，本质就是某个服务器上的自子目录！
    //想表达的意思，客户端只能请求相对根目录中的资源！其他地方的资源就不予理会！
    // /../login但是也有这样的写法！————..会让路径的查找跑到根目录之外！这样是不合理，不安全的！
    static bool ValidPath(const std::string &path)
    {
        // 判断思想计算目录深度！以/作为间隔！来进行字符串的分割！
        // 来判断有多少子目录！那么就有多少层！
        // 深度是不能小于0的！
        std::vector<std::string> substr;
        Split(path, "/", &substr);
        int level = 0;
        for (auto &dir : substr)
        {
            if (dir == "..")
            {
                --level; // 任意一层走出相对根目录就说明有问题！
                if (level < 0)
                    return false;
                continue;
            }
            ++level;
        }
        return true;
    }
};





class HttpRequest
{
public:
    std::string _method;//请求方法
    std::string _path;//资源路径
    std::string _version;//协议版本
    std::string _body;//请求正文
    std::smatch _matches;//资源路径的正则提取数据！
    std::unordered_map<std::string,std::string> _headers;//头部字段
    std::unordered_map<std::string,std::string> _params;//查询字符串
public:
    HttpRequest()
        : _version("HTTP/1.1")
    {}
    // 每次上下文被处理完毕后就要重置一下！防止某些字段对后续请求造成干扰！
    void ReSet()
    {
        _method.clear();
        _path.clear();
        _version = "HTTP/1.1";
        _body.clear();
        std::smatch match;
        _matches.swap(match);
        _headers.clear();
        _params.clear();
    }


    //插入头部字段
    void SetHeader(const std::string& key,const std::string &val)
    {
        _headers.insert({key,val});
    }
    //判断指定头部字段是否存在！
    bool HasHeader(const std::string& key)const
    {
        auto it = _headers.find(key);
        if(it == _headers.end())
            return false;
        return true;
    }
    //获取指定头部字段的值
    std::string GetHeader(const std::string & key)const
    {
        auto it = _headers.find(key);
        if(it == _headers.end())
            return "";
        return it->second;
    }
    //插入查询字段！
    void  SetParam(const std::string& key,const std::string& val)
    {
        _params.insert({key,val});
    }

    //判断是不是有指定的查询字符串
    bool HasParam(const std::string& key)const
    {
        auto it = _params.find(key);
        if(it == _params.end())
            return false;
        return false;
    }

    //获取指定的查询字符串！
    std::string GetParam(const std::string&key)const
    {
        auto it = _params.find(key);
        if(it == _params.end())
            return "";
        return it->second;
    }

    //获取正文长度
    size_t ContextLength()const
    {
        //Content-length:1231\r\n 
        
        bool ret = HasHeader("Content-Length");
        if(ret == false)//没有找到就说明没有正文！
            return 0;
        std::string clen = GetHeader("Content-Length");
        return std::stol(clen);
    }

    //判断是不是短连接
    bool Close() const
    {
        //没有Connection字段或者有却是close的，则都是短连接，否则就是长连接
        if(HasHeader("Connection") == true && GetHeader("Connection") == "keep-alive")
            return true;
        return false;

    }
};


class HttpResponse
{
public:
    int _status;//响应状态码
    bool _redirect_flag;//重定向标志！
    std::string _body;//响应正文
    std::string _redirect_url;//重定向后的新的url地址！
    std::unordered_map<std::string,std::string> _headers;//保存头部字段
public:
    HttpResponse()
        :_redirect_flag(false),_status(200)
    {}

    HttpResponse(int status)
        :_redirect_flag(false),_status(status)
    {}
    

    //重置
    void ReSet()
    {
        _status = 200;
        _redirect_flag = false;
        _body.clear();
        _redirect_url.clear();
        _headers.clear();
    }

    //插入头部字段
    void SetHeader(const std::string& key,const std::string &val)
    {
        _headers.insert({key,val});
    }
    //判断指定头部字段是否存在！
    bool HasHeader(const std::string& key)
    {
        auto it = _headers.find(key);
        if(it == _headers.end())
            return false;
        return false;
    }
    //获取指定头部字段的值
    std::string GetHeader(const std::string & key)
    {
        auto it = _headers.find(key);
        if(it == _headers.end())
            return "";
        return it->second;
    }

    //设置正文
    void SetContent(const std::string& body,const std::string & type = "text/html")
    {
        _body = body;
        SetHeader("Content-Type",type);
    }

    //设置重定向！
    void SetRedirect(const std::string& url,int status = 302)
    {
        _status= status;
        _redirect_flag = true;
        _redirect_url = url;
    }

    //长短链接的判断！
    bool Close()
    {
        //没有Connection字段或者有却是close的，则都是短连接，否则就是长连接
        if(HasHeader("Connection") == true && GetHeader("Connection") == "keep-alive")
            return true;
        return false;

    }
};


typedef enum
{
    RECV_HTTP_ERROR,
    RECV_HTTP_LINE,
    RECV_HTTP_HEAD,
    RECV_HTTP_BODY,
    RECV_HTTP_OVER
}HttpRecvStatus;


#define MAX_LINE 8192
class HttpContext
{
private:
    int _resp_status;//响应状态码
    HttpRecvStatus _recv_status;//当前接收和解析的状态
    HttpRequest _request;//已经解析完毕得到的请求信息！
private:
    //解析请求行
    bool ParseHttpLine(const std::string& line)
    {

        std::smatch matchs;

        std::regex e ("(GET|HEAD|POST|PUT|DELETE) ([^?]*)(?:\\?(.*))? (HTTP/1\\.[01])(?:\n|\r\n)?",std::regex::icase);
        //icase是为了忽略大小写！防止出现GET能解析，但是get不能解析的情况

        bool ret = std::regex_match(line,matchs,e);

        if(ret == false)
        {
            _recv_status = RECV_HTTP_ERROR;
            _resp_status= 400;//BAD REQUEST
            return false;
        }
        //matchs[0]就是url本身
        //matchs[1] 请求方法
        //matchs[2] 资源路径
        //matchs[3] 是查询字符串！——一般都是xxxx=yyyy&zzzz=qqqq的形式！
        //matchs[4] 是协议版本

        //请求方法的获取
        _request._method = matchs[1];
        //将可能的小写都转化为大写！
        std::transform(_request._method.begin(),_request._method.end(),_request._method.begin(),::toupper);

        //资源路径的获取！必须进行解码操作！但是不需要加号转空格！
        _request._path = Util::UrlDecode(matchs[2],false);//要求资源路径进行解码！
        //协议版本的获取！
        _request._version = matchs[4];

        //查询字符串的获取与处理！
        std::vector<std::string> query_string_arry;
        std::string query_string = matchs[3];
        //查询字符串的格式一般都是 key=value&key1=value2...这种格式
        Util::Split(query_string,"&",&query_string_arry);
        //针对各个字串，以=进行风格得到key与val，但是得到之后也要进行解码！
        for(auto& str :query_string_arry)
        {
            //将每一个元素用等号进行分割！
            size_t pos = str.find("=");
            if(pos  == std::string::npos)//没有等号说明有问题！
            {
                _recv_status = RECV_HTTP_ERROR;
                _resp_status = 400; // BAD REQUEST
                return false;
            }
            std::string key =Util::UrlDecode(str.substr(0,pos),true);
            std::string val = Util::UrlDecode(str.substr(pos + 1), true);
            //https://www.baidu.com/s?ie=UTF-8&wd=C%26%26%26一般都是加码过的！所以必须解码！
            _request.SetParam(key,val);
        }
        return true;
    }

    //获取请求行
    bool RecvHttpLine(Buffer* buf)
    {
        if(_recv_status != RECV_HTTP_LINE) return false;
        //获取一行数据
        std::string line = buf->GetLineAndPop();

        //需要考虑的要素：缓冲区的数据不是一行或者一行的数据超级大
        if(line.size()==0)
        {
            //缓冲区的数据不足一行！
            //则需要判断缓冲区的可读数据长度有多长！——如果缓冲区的数据十几m了！但是还没有一行！这就是有问题的！
            if(buf->ReadAbleSize() > MAX_LINE)
            {
                _recv_status = RECV_HTTP_ERROR;
                _resp_status = 414;//URL TOO LONG!
                return false;
            }
            //缓冲区的数据不足一行！但是也不多！那么就等待新数据的到来！
            return true;
        }

        if (line.size()> MAX_LINE)//虽然有一行但是太长了！也不行！
        {
            _recv_status = RECV_HTTP_ERROR;
            _resp_status = 414; // URL TOO LONG!
            return false;
        }

       bool ret =  ParseHttpLine(line);
       if(ret == false)
            return false;

        //首行处理完毕！进入头部获取阶段！
        _recv_status = RECV_HTTP_HEAD;
        return true;
    }

    bool ParseHttpHeader(const std::string line)
    {
        // 头部格式： key: val\r\nkey: val\r\n
        size_t pos = line.find(": ");
        if (pos == std::string::npos) // 没有找到说明格式出现问题了！
        {
            _recv_status = RECV_HTTP_ERROR;
            _resp_status = 400; // BAD REQUEST
            return false;
        }
        std::string key = line.substr(0, pos);
        std::string val = line.substr(pos + 2);//因为是两个间隔所以是+2
        _request.SetHeader(key,val);
        return true;
    }

    bool  RecvHttpHeader(Buffer *buf)
    {
        if (_recv_status != RECV_HTTP_HEAD) return false;
        //一行一行的取出数据，知道遇到空行为止！
        //头部格式： key:val\r\nkey: val: \r\n
        while(1)//因为头部不止一行！所以要循环！
        {
            std::string line = buf->GetLineAndPop();
            if (line.size() == 0)
            {
                if (buf->ReadAbleSize() > MAX_LINE)
                {
                    _recv_status = RECV_HTTP_ERROR;
                    _resp_status = 414; // URL TOO LONG!
                    return false;
                }
                return true;
            }

            if (line.size() > MAX_LINE) // 虽然有一行但是太长了！也不行！
            {
                _recv_status = RECV_HTTP_ERROR;
                _resp_status = 414; // URL TOO LONG!
                return false;
            }

            if(line == "\n" || line=="\r\n")//遇到了空行！
                break;

            bool ret = ParseHttpHeader(line);
            if (ret == false)
                return false;
        }
        //头部获取完毕！进入正文获取阶段！
        _recv_status = RECV_HTTP_BODY;
        return true;
    }


    bool RecvHttpBody(Buffer* buf)
    {
        if(_recv_status != RECV_HTTP_BODY) return false;
        //1.获取正文长度！
        size_t content_length = _request.ContextLength();
        if(content_length == 0) //没有正文则请求接收完毕！
        {
            _recv_status = RECV_HTTP_OVER;
            return true;
        }
        //2.接收正文就是存在正文都保存在_request.body里面
        size_t real_len = content_length -_request._body.size();//实际要接收的正文长度！
        //3.也要考虑当前的缓冲区的正文是不是全部的正文！
        //缓冲区的数据包含了当前请求的所有正文！则取出所有的所需的数据！
        if(buf->ReadAbleSize() >= real_len)
        {
            _request._body.append(buf->GetReadPosition(),real_len);
            buf->MoveReadOffset(real_len);
            _recv_status = RECV_HTTP_OVER;
            return true;
        }
        //缓冲区的数据无法满足正文的需要即数据不足！取出数据放入body中，等待新数据的到来！
        //但是不用修改状态因为后面还要接收正文！
        _request._body.append(buf->GetReadPosition(), buf->ReadAbleSize());
        buf->MoveReadOffset(buf->ReadAbleSize());
        return true;
    }

public:
    HttpContext()
        : _recv_status(RECV_HTTP_LINE),_resp_status(200)
    {}

    void ReSet()
    {
        _request.ReSet();
        _recv_status = RECV_HTTP_LINE; 
        _resp_status = 200;
    }

    //获取响应状态码！
    int ResponseStatus()
    {
        return _resp_status;
    }

    HttpRecvStatus RecvStatus()
    {
        return _recv_status;
    }

    //获取一个请求
    HttpRequest& Request()
    {
        return _request;
    }

    //接收并解析一个Http请求
    void RecvHttprequest(Buffer* buf)
    {
        //不同的状态做不同的事情！但是这里不要break！
        switch (_recv_status)//根据手状态来决定当前要进行那一步！
        {
        case RECV_HTTP_LINE:
            RecvHttpLine(buf);//不用break！因为首行结束后应该立刻处理头部！
        case RECV_HTTP_HEAD:
            RecvHttpHeader(buf);//头部完毕后应该接收正文！
        case RECV_HTTP_BODY:
            RecvHttpBody(buf);  
        }
    }
};


class HttpServer
{
private:
    using Handler = std::function<void(const HttpRequest&,HttpResponse*)>;
    using Handlers = std::vector<std::pair<std::regex,Handler>>;
    Handlers _get_route;
    Handlers _post_route;
    Handlers _put_route;
    Handlers _delete_route;
    std::string _basedir;//静态资源根目录！
    TcpServer _server;

private:

    void ErrorHandler(const HttpRequest& req,HttpResponse* rsp)
    {
        //1.组织一个错误展示页面！
        std::string body;
        body+="<html>";
        body+="<head>";
        body+="<meta http-equiv='Content-Type' content='text/html;charset=utf-8'>";
        body+="</head>";
        body+="<body>";
        body+="<h1>";
        body+=std::to_string(rsp->_status);
        body+=" ";
        body+=Util::StatusDesc(rsp->_status);
        body+="</h1>";
        body+="</body>";
        body+="</html>";

        //2.将页面数据，当做响应正文，放入rsp中！
        rsp->SetContent(body,"text/html");
    }

    bool IsFileHandler(const HttpRequest& req)
    {
        // 1.必须设置了静态资源根目录！
        if (_basedir.empty())
            return false;
        // 2.请求方法，必须是GET / HEAD请求
        if (req._method != "GET" && req._method != "HEAD")
            return false;
        // 3.请求的资源路径必须是一个合法路径,不能超出相对根目录
        if (Util::ValidPath(req._path) == false)
            return false;

        // 4.请求的资源必须存在！且必须是普通文件
        // 有一种请求比较特殊就是  / 就是请求根目录！没有任何其他的资源 例如： / 或者 /image/
        // 这种情况默认追加上index.html
        std::string req_path = _basedir + req._path; // 为了避免直接修改请求的资源路径，因为要定义一个临时文件
        //要加上_basedir才是一个实际的路径！例如： /image/a.png ——> ./wwwroot/image/a.png
        if (req._path.back() == '/')
            req_path += "index.html";

        if (Util::IsRegular(req_path) == false)
            return false;

        return true;
    }

    //这是静态资源请求的处理
    void FileHandler(HttpRequest& req,HttpResponse* rsp)
    {
        std::string req_path = _basedir + req._path; // 为了避免直接修改请求的资源路径，因为要定义一个临时文件
        //要加上_basedir才是一个实际的路径！例如： /image/a.png ——> ./wwwroot/image/a.png
        if (req._path.back() == '/')
            req_path += "index.html";

        //将静态资源文件的数据读取出来，放入到rsp的_body中
        //并设置mine
        bool ret = Util::ReadFile(req_path,rsp->_body);
        if(ret == false)
            return;
        
        std::string mime = Util::ExMime(req_path);
        rsp->SetHeader("Content-Type",mime);
        return;
    }

    // 将HttpResponse中的要素按照Http协议格式进行组织！
    void WrietResponse(const PtrConnection &conn,const HttpRequest &req, HttpResponse &rsp)
    {
        //1.先完善头部字段！
        if(req.Close() == true)
            rsp.SetHeader("Connection","close");
        else 
            rsp.SetHeader("Connection","keep-alive");

        if(rsp._body.empty() == false && rsp.HasHeader("Content-Length") == false)
        {
            //正文不为空！且没有正文长度字段！那么就要添加！
            rsp.SetHeader("Content-Length",std::to_string(rsp._body.size()));
        }
        
        if(rsp._body.empty() == false && rsp.HasHeader("Content-Type") == false)
        {
            //设置类型为二进制流！
            rsp.SetHeader("Content-Type","application/octet-stream");
        }
        if(rsp._redirect_flag == true)//如果用户设置了重定向！
        {
            rsp.SetHeader("Location",rsp._redirect_url);
        }
        //2.将rsp的要素，按照Http格式进行组织！
        std::stringstream rsp_str;
        rsp_str  << req._version << " " << std::to_string(rsp._status) << " " << Util::StatusDesc(rsp._status) << "\r\n";
        for(auto & head :rsp._headers)
        {
            rsp_str << head.first << ": " << head.second << "\r\n";
        }
        rsp_str << "\r\n";//添加空行
        rsp_str << rsp._body;

        //3.发送数据！
        conn->Send(&rsp_str.str()[0],rsp_str.str().size());
    }
    //功能性请求进行分类操作
    void Dispatcher(HttpRequest& req,HttpResponse* rsp,Handlers& handlers)
    {
        //在对应的请求方法的路由表中查找对应的请求是否含有对应请求的处理函数！
        //有则调用！没有则将状态设置为404
        //思想：路由表存储的是键值对——正则表达式 & 处理函数！
        //使用正则表达式对请求资源的路径进行正则匹配！匹配成功就使用对应的函数进行处理
        // 例如：numbers/(\d+)  /number/12314
        for(auto & handler :handlers)
        {
            const std::regex& re = handler.first;
            const Handler& functor = handler.second;//处理函数
            bool ret = std::regex_match(req._path,req._matches,re);
            if(ret == false)
             continue;
             return functor(req,rsp);//传入请求信息，和空的rsp执行处理函数！
        }
        rsp->_status = 404;
    }


    //对请求进行路由匹配！
    void Route(HttpRequest& req,HttpResponse* rsp)
    {
        //对请求进行分辨！——是静态资源请求还是一个功能性请求！
        //  静态资源请求！则进行静态资源的处理
        //  功能性请求，则需要通过几个请求路由表来确定是是否有处理函数！
        //  既不是静态资源请求也没有设置对应的功能性请求函数，就返回404
        //GET HEAD 都默认先认为是静态资源请求！
        if(IsFileHandler(req) == true)
        {
            //是一个静态资源请求！
            return FileHandler(req, rsp);
        }
        if(req._method == "GET" || req._method =="HEAD")
            return Dispatcher(req,rsp,_get_route);
        else if(req._method == "POST")
            return Dispatcher(req,rsp,_post_route);
        else if(req._method == "PUT")
            return Dispatcher(req,rsp,_put_route);
        else if(req._method == "DELETE")
            return Dispatcher(req,rsp,_delete_route);
        //如果到这里这里都没有！那么久将状态码设置为404
        rsp->_status = 405;//method not allowed
    }


    //设置上下文
    void OnConnected(const PtrConnection& conn)
    {
        DBG_LOG("CONTEXT");
        conn->SetContext(HttpContext());
        DBG_LOG("NEW CONNECTION %p",conn->GetContext()->get<HttpContext>());
    }

    //缓冲区数据解析+处理的过程！
    void OnMessage(const PtrConnection& conn,Buffer* buf)
    {
        while(buf->ReadAbleSize() > 0)
        {
            // 1.获取上下文
            HttpContext *context = conn->GetContext()->get<HttpContext>();
            // 2.通过上下文对缓冲区进行解析得到HttpRequest对象
            // 如果缓冲区的数据解析出错，就直接回复出错响应！
            // 如果解析正常，且请求获取完毕才开始进行处理！
            context->RecvHttprequest(buf);
            HttpResponse rsp(context->ResponseStatus());//!!!!!!!!!!!!!!!!!!不要设置错误了！
            HttpRequest &req = context->Request();

            if (context->RecvStatus() >= 400)
            {
                // 进行错误响应和关闭连接！
                 ErrorHandler(req,&rsp);
                // 填充一个错误显示页面！
                WrietResponse(conn, req, rsp); // 将组织响应发送给客户端！
                conn->Shutdown();
                return;
            }
            if (context->RecvStatus() != RECV_HTTP_OVER) // 当前请求还没有接收完整则退出等有新数据到来再进行处理！
            {
                return;
            }
            // 3.使用路由处理请求得到HttpResponse对象
            Route(req, &rsp);

            // 4.对HttpResponse进行组织发送
            WrietResponse(conn, req, rsp);

            //5.重置上下午
            context->ReSet();
            // 6，根据长短链接判断是否关闭链接或者处理

            if (rsp.Close() == true) // 如果是一个短连接
                conn->Shutdown();    // 直接关闭连接
            
        }
        return;
    }

    void EnableInactiveRelease(int timeout)
    {
        _server.EnableActiveRelease(timeout);
    }


public:
    HttpServer(int port,int timeout = DEFAULT_TIMEOUT)
        : _server(port)
    {
        EnableInactiveRelease(timeout);
        _server.SetConnectedCallback(std::bind(&HttpServer::OnConnected,this,std::placeholders::_1));
        _server.SetMessageCallback(std::bind(&HttpServer::OnMessage,this,std::placeholders::_1,std::placeholders::_2));
    }
    //设置静态根目录！
    void SetBaseDir(const std::string& path)
    {
        assert(Util::IsDirectory(path) == true);
        _basedir = path;
    }

    //将Get/Post/Put/Delete方法假如到get路由表里面去！
    void Get(const std::string& pattern,const Handler& handler)
    {
        _get_route.push_back({std::regex(pattern),handler});
    }
    void Post(const std::string& pattern,const Handler& handler)
    {
        _post_route.push_back({std::regex(pattern),handler});
    }
    void Put(const std::string& pattern,const Handler& handler)
    {
        _put_route.push_back({std::regex(pattern),handler});
    }
    void Delete(const std::string& pattern,const Handler& handler)
    {
        _delete_route.push_back({std::regex(pattern),handler});
    }

    void SetThreadCount(int count)
    {
        _server.SetThreadCount(count);
    }


    void Listen()
    {
        _server.Start();
    }

};


